package com.hmdp.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.hmdp.domain.dto.Result;
import com.hmdp.domain.dto.ScrollResult;
import com.hmdp.domain.dto.UserDTO;
import com.hmdp.domain.entity.*;
import com.hmdp.domain.vo.BlogVO;
import com.hmdp.mapper.BlogLikeMapper;
import com.hmdp.mapper.BlogMapper;
import com.hmdp.service.*;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmdp.utils.SystemConstants;
import com.hmdp.utils.UserHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

import static com.hmdp.utils.RedisConstants.BLOG_LIKED_KEY;
import static com.hmdp.utils.RedisConstants.FEED_KEY;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author 虎哥
 * @since 2021-12-22
 */
@Service
@Slf4j
public class BlogServiceImpl extends ServiceImpl<BlogMapper, Blog> implements IBlogService {
    @Autowired
    IUserService userService;
    @Autowired
    IFollowService followService;
    @Autowired
    StringRedisTemplate stringRedisTemplate;
    @Autowired
    BlogLikeService blogLikeService;
    @Autowired
    BlogLikeMapper blogLikeMapper;
    @Autowired
    IShopService shopService;
    @Autowired
    IBlogCommentsService blogCommentsService;
    private static final DefaultRedisScript<Boolean> IS_BLOG_LIKE_SCRIPT = new DefaultRedisScript<>();

    static {
        IS_BLOG_LIKE_SCRIPT.setLocation(new ClassPathResource("blogLike.lua"));
        IS_BLOG_LIKE_SCRIPT.setResultType(Boolean.class);
    }

    private final String flag = "1";

    /**
     * 分页查询当前页数的博客信息
     *
     * @param current
     * @return
     */
    @Override
    public Result queryHotBlog(Integer current) {
        log.info("分页查询博客,页数：{}", current);
        // 根据用户查询
        Page<Blog> page = query()
                .orderByDesc("create_time")
                .page(new Page<>(current, SystemConstants.MAX_PAGE_SIZE));
        // 获取当前页数据
        List<Blog> records = page.getRecords();
        ArrayList<BlogVO> blogVOS = new ArrayList<>();
        // 查询用户
        records.forEach((blog) -> blogVOS.add(blogToBlogVo(blog)));
        return Result.ok(records);
    }

    private BlogVO blogToBlogVo(Blog blog) {
        BlogVO blogVO = new BlogVO();
        BeanUtil.copyProperties(blog, blogVO, true);
        //发表这篇博客的用户信息
        blogUserSingleInfo(blog, blogVO);
        //查询点赞数
        blogLikes(blogVO);
        //查询评论数
        blogCommonts(blogVO);
        //查看当前用户是否点赞
        cunrrentUserIsLike(blogVO);
        return blogVO;
    }

    /**
     * 发表博客的用户展示信息
     *
     * @param blog
     * @param blogVO
     */
    private void blogUserSingleInfo(Blog blog, BlogVO blogVO) {
        if (blogVO.getUserId() == null) {
            return;
        }
        User userBlog = userService.getById(blog.getUserId());
        blogVO.setName(userBlog.getNickName());
        blogVO.setIcon(userBlog.getIcon());
    }

    /**
     * 博客点赞数
     *
     * @param blogVO
     */
    private void blogLikes(BlogVO blogVO) {
        if (blogVO.getId() == null) {
            return;
        }
        QueryWrapper<BlogLike> blogLikeServiceQueryWrapper = new QueryWrapper<>();
        blogLikeServiceQueryWrapper.eq("blog_id", blogVO.getId());
        blogVO.setLiked(blogLikeService.count(blogLikeServiceQueryWrapper));
    }

    /**
     * 博客评论数
     *
     * @param blogVO
     */
    private void blogCommonts(BlogVO blogVO) {
        if (blogVO.getId() == null) {
            return;
        }
        QueryWrapper<BlogComments> blogCommentsQueryWrapper = new QueryWrapper<>();
        blogCommentsQueryWrapper.eq("blog_id", blogVO.getId());
        blogVO.setComments(blogCommentsService.count(blogCommentsQueryWrapper));
    }

    /**
     * 当前勇士是否对博客点过赞
     *
     * @param blogVO
     */
    private void cunrrentUserIsLike(BlogVO blogVO) {
        if (blogVO.getId() == null) {
            return;
        }
        QueryWrapper<BlogLike> blogLikeQueryWrapper = new QueryWrapper<>();
        blogLikeQueryWrapper.eq("blog_id", blogVO.getId()).eq("user_id", UserHolder.getUser().getId());
        blogVO.setIsLike(blogLikeService.count(blogLikeQueryWrapper) > 0);
    }

    /**
     * 通过id查博客
     *
     * @param id
     * @return
     */
    @Override
    public Result queryBlogById(Long id) {
        Blog blog = getById(id);
        if (blog == null) {
            return Result.fail("笔记不存在");
        }
        return Result.ok(blog);
    }

    /**
     * 点赞
     *
     * @param id
     * @return
     */
    @Override
    public Result likeBlog(Long id, String status) {
        Long userId = UserHolder.getUser().getId();
        BlogLike data = new BlogLike();
        data.setBlogId(Math.toIntExact(id));
        data.setUserId(Math.toIntExact(userId));
        stringRedisTemplate.execute(IS_BLOG_LIKE_SCRIPT,
                Collections.emptyList(),
                JSONUtil.toJsonStr(data), status, id.toString());
        return Result.ok();
    }


    @Override
    public Result saveBlog(Blog blog) {
        // 获取登录用户
        Long userId = UserHolder.getUser().getId();
        blog.setUserId(userId);
        // 保存探店博文
        boolean isSuccess = save(blog);
        if (!isSuccess) {
            return Result.fail("新增笔记失败");
        }
        List<Follow> follows = followService.query().eq("follow_user_id", userId).list();
        //使用推模式，将发布博客的消息(博客Id)推送给所有粉丝
        for (Follow follow : follows) {
            Long followUserId = follow.getUserId();
            String key = FEED_KEY + followUserId;
            stringRedisTemplate.opsForZSet().add(key, blog.getId().toString(), System.currentTimeMillis());
        }
        // 返回id
        return Result.ok(blog.getId());
    }

    @Override
    public Result queryBlogOfFollow(Long max, Integer offset) {
        Long userId = UserHolder.getUser().getId();
        //查询收件箱，滚动查询，每次过来都需要知道上次查询的位置，也就是时间戳，和需要跳过多少相同时间戳的参数，这样才能实现在下次查询不重复
        Integer count = 2;
        Set<ZSetOperations.TypedTuple<String>> typedTuples =
                stringRedisTemplate.opsForZSet().reverseRangeByScoreWithScores(FEED_KEY + userId, 0, max, offset, count);
        if (typedTuples == null || typedTuples.isEmpty()) {
            return Result.ok();
        }
        Long minTime = 0L;
        Integer os = 1;
        ArrayList<Long> blogIds = new ArrayList<>(typedTuples.size());
        for (ZSetOperations.TypedTuple<String> typedTuple : typedTuples) {
            blogIds.add(Long.valueOf(typedTuple.getValue()));
            long longValue = Objects.requireNonNull(typedTuple.getScore()).longValue();
            if (minTime.equals(longValue)) {
                os++;
            } else {
                os = 1;
                minTime = typedTuple.getScore().longValue();
            }
        }
        String idStr = StrUtil.join(",", blogIds);
        List<Blog> blogs = query().in("id", blogIds).last("order by field(id," + idStr + ")").list();
        ArrayList<BlogVO> blogVOS = new ArrayList<>();
        blogs.forEach(blog -> {
            BlogVO blogVO = blogToBlogVo(blog);
            blogVOS.add(blogVO);
        });
        ScrollResult scrollResult = new ScrollResult();
        scrollResult.setList(blogVOS);
        scrollResult.setOffset(os);
        scrollResult.setMinTime(minTime);


        return Result.ok(scrollResult);
    }

    @Override
    public Result likeTop5() {
        Long zsetSize = stringRedisTemplate.opsForZSet().size(BLOG_LIKED_KEY);
        int start = Math.toIntExact(zsetSize - 5);
        int end = Math.toIntExact(zsetSize);
        if (zsetSize <= 5) {
            start = 0;
        }
        Set<String> top5 = stringRedisTemplate.opsForZSet().reverseRange(BLOG_LIKED_KEY, start, end);
        if (!CollUtil.isEmpty(top5)) {
            List<Long> ids = top5.stream().map(Long::valueOf).collect(Collectors.toList());
            List<Blog> blogs = query().in("id", ids).last("order by field(id," + StrUtil.join(",", ids) + ")").list();
            ArrayList<BlogVO> blogVOS = new ArrayList<>();
            blogs.forEach(blog -> {
                blogVOS.add(blogToBlogVo(blog));
            });
            return Result.ok(blogVOS);
        }
        return Result.fail("没有数据");
    }

    /**
     * 点赞统计数据缓存预热
     */
    @PostConstruct
    public void cachedReservation() {
        //从数据库中统计所有的点赞
        Set<ZSetOperations.TypedTuple<String>> blogLikes = blogLikeMapper.countBlogLike();
        stringRedisTemplate.opsForZSet().add(BLOG_LIKED_KEY, blogLikes);
        log.info("点赞数据缓存完成");
    }

}
